/*
	by Ilija
*/


#include "GLTerrain.h"


void GLTerrain::init(s32 sizeX, s32 sizeY)
{
	s32 vx1 = 0;
	s32 vx2 = sizeX;
	s32 vy1 = 0;
	s32 vy2 = sizeX;
	for (s32 vx = vx1; vx <= vx2; vx++)
	{
		for (s32 vy = vy1; vy <= vy2; vy++)
		{
			//init vertex
			m_VertexMap[vx][vy].height = 0.0f;
			m_VertexMap[vx][vy].color = Color(0, 128, 0);
			//init map
			if (vx < sizeX && vy < sizeY)
			{
				m_FaceMap[vx][vy].flags = 0;
			}
		}
	}
	m_SizeX = sizeX;
	m_SizeY = sizeY;
	buildNormals();
}


void GLTerrain::generate(s32 sizeX, s32 sizeY)
{
	init(sizeX, sizeY);

	s32 n = 50;

	for (s32 i = 0; i < n; i++)
	{
		f32 x = randomFloat(m_SizeX);
		f32 y = randomFloat(m_SizeY);
		f32 bsize = randomFloat(30, 50);
		f32 dh = randomFloat(-1, 2);
		addHeight(x, y, bsize, dh);
		if (dh > 0)
			addColor(x, y, bsize * 2, dh * 10, dh * 20, dh * 10);
	}

	buildNormals();
}


void GLTerrain::addHeight(f32 x, f32 y, f32 bsize, f32 dh)
{
    s32 x1 = mMax((s32)x-(s32)bsize-2, 0);
    s32 y1 = mMax((s32)y-(s32)bsize-2, 0);
    s32 x2 = mMin((s32)x+(s32)bsize+2, m_SizeX);
    s32 y2 = mMin((s32)y+(s32)bsize+2, m_SizeY);
    for (s32 i=x1; i<=x2; i++)
    for (s32 j=y1; j<=y2; j++)
    {
        float r = mDist(x, y, i, j) * 2 / bsize;
        if (mAbs(r) < 1.0)
        {
            m_VertexMap[i][j].height += dh/2 + cos(r*3.14)*dh/2;
        }
    }
    buildNormals();
}


void GLTerrain::addColor(f32 x, f32 y, f32 bsize, f32 dR, f32 dG, f32 dB)
{
    s32 x1 = mMax((s32)x-(s32)bsize-2, 0);
    s32 y1 = mMax((s32)y-(s32)bsize-2, 0);
    s32 x2 = mMin((s32)x+(s32)bsize+2, m_SizeX);
    s32 y2 = mMin((s32)y+(s32)bsize+2, m_SizeY);
    for (s32 i=x1; i<=x2; i++)
    for (s32 j=y1; j<=y2; j++)
    {
        float r = 1 - mDist(x, y, i, j) / bsize;
        if (r <= 1 && r >= 0)
        {
        	Color& cc = m_VertexMap[i][j].color;
            cc.R = mClamp(cc.R + (r * dR), 0, 255);
            cc.G = mClamp(cc.G + (r * dG), 0, 255);
            cc.B = mClamp(cc.B + (r * dB), 0, 255);
        }
    }
}


f32 GLTerrain::pickHeight(f32 x, f32 y)
{
	x = mClamp(x, 0, m_SizeX);
    y = mClamp(y, 0, m_SizeY);
    int ix = (int)x;
    int iy = (int)y;
    float xx = x - ix;
    float yy = y - iy;
    vector3f a1, a2, b1, b2;
    vector3f q0, q, p;

    //Q_0
    if (xx > yy)
		a1.set(1, 0, 0);
    else
		a1.set(0, 1, 0);
    a2.set(xx, yy, 0);
    b1.set(0, 0, 0);
    b2.set(1, 1, 0);
    if (!v3Intersect(q0, a1, a2, b1, b2)) return 0;
    //Q
    a1 = q0;
    a2.set(q0.x, q0.y, q0.z+1);
    b1.set(0, 0, m_VertexMap[ix][iy].height);
    b2.set(1, 1, m_VertexMap[ix+1][iy+1].height);
    if (!v3Intersect(q, a1, a2, b1, b2)) return 0;
    //P'
    a1 = q;
    if (xx > yy)
		a2.set(1, 0, m_VertexMap[ix+1][iy].height);
    else
		a2.set(0, 1, m_VertexMap[ix][iy+1].height);
    b1.set(xx, yy, 0);
    b2.set(xx, yy, 1);
    if (!v3Intersect(p, a1, a2, b1, b2)) return 0;

    return p.z;
}

void GLTerrain::onRender()
{
	s32 tx1 = 0;
	s32 tx2 = m_SizeX;
	s32 ty1 = 0;
	s32 ty2 = m_SizeY;

	glEnableTexture(m_Texture);

	glBegin(GL_QUADS);

	for (s32 tx = tx1; tx < tx2; tx++)
	{
		for (s32 ty = ty1; ty < ty2; ty++)
		{
			if (m_FaceMap[tx][ty].flags != 1)
			{
				//A
				glColor(m_VertexMap[tx][ty].color);
				glNormal3fv(m_VertexMap[tx][ty].normal.getPtr());
				glTexCoord2f(0, 0);
				glVertex3f(tx, ty, m_VertexMap[tx][ty].height);
				//B
				glColor(m_VertexMap[tx+1][ty].color);
				glNormal3fv(m_VertexMap[tx+1][ty].normal.getPtr());
				glTexCoord2f(1, 0);
				glVertex3f(tx+1, ty, m_VertexMap[tx+1][ty].height);
				//C
				glColor(m_VertexMap[tx+1][ty+1].color);
				glNormal3fv(m_VertexMap[tx+1][ty+1].normal.getPtr());
				glTexCoord2f(1, 1);
				glVertex3f(tx+1, ty+1, m_VertexMap[tx+1][ty+1].height);
				//D
				glColor(m_VertexMap[tx][ty+1].color);
				glNormal3fv(m_VertexMap[tx][ty+1].normal.getPtr());
				glTexCoord2f(0, 1);
				glVertex3f(tx, ty+1, m_VertexMap[tx][ty+1].height);
			}
		}
	}

	glEnd();

	glDisableTexture();
}



void GLTerrain::buildNormals()
{
	s32 nx1 = 0;
	s32 nx2 = TERRAIN_MAX_SIZE;
	s32 ny1 = 0;
	s32 ny2 = TERRAIN_MAX_SIZE;

	for (s32 tx = nx1; tx < nx2; tx++)
	{
		for (s32 ty = ny1; ty < ny2; ty++)
		{
			vector3f v1(tx, ty, m_VertexMap[tx][ty].height);
			vector3f v2(tx+1, ty, m_VertexMap[tx+1][ty].height);
			vector3f v3(tx+1, ty+1, m_VertexMap[tx+1][ty+1].height);
			vector3f v4(tx, ty+1, m_VertexMap[tx][ty+1].height);
			m_FaceMap[tx][ty].normal1 = v3MakeFaceNormal(v1, v2, v3);
			m_FaceMap[tx][ty].normal2 = v3MakeFaceNormal(v3, v4, v1);
		}
	}

	for (s32 vx = nx1; vx <= nx2; vx++)
	{
		for (s32 vy = ny1; vy <= ny2; vy++)
		{
			computeVerticeNormal(vx, vy);
		}
	}
}


void GLTerrain::computeVerticeNormal(s32 vx, s32 vy)
{
	vector3f& norm = m_VertexMap[vx][vy].normal;
	s32 n = 0;

    if (vx > 0 && vy > 0)
    {
    	norm += m_FaceMap[vx-1][vy-1].normal1;
    	norm += m_FaceMap[vx-1][vy-1].normal2;
    	n += 2;
    }
    if (vx > 0 && vy < m_SizeY)
    {
    	norm += m_FaceMap[vx-1][vy].normal1;
    	n++;
    }
    if (vx < m_SizeX && vy > 0)
    {
    	norm += m_FaceMap[vx][vy-1].normal2;
    	n++;
    }
    if (vx < m_SizeX && vy < m_SizeY)
    {
    	norm += m_FaceMap[vx][vy].normal1;
    	norm += m_FaceMap[vx][vy].normal2;
    	n += 2;
    }

    norm /= (f32)n;
    norm.normalize();
}


